#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef double db;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+ch-'0'; ch=getchar(); }
	return x*f;
}
const int MAXN=1010;
int n,f[MAXN],match[MAXN],head[MAXN],cnt,ans;
bool vis[MAXN];
struct point
{
	int x,y,z;
	bool operator<(const point b)const
	{
		return x==b.x?(y==b.y?z<b.z:y<b.y):x<b.x;
	}
}p[MAXN];
struct edge
{
	int v,next;
}e[MAXN*MAXN];
void addedge(int x,int y)
{
	e[++cnt]=(edge){y,head[x]};
	head[x]=cnt;
	return;
}
bool Hungary(int x)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int y=e[i].v;
		if(vis[y])continue;
		vis[y]=true;
		if(!match[y]||Hungary(match[y]))
		{
			match[y]=x;
			return true;
		}
	}
	return false;
}
int main()
{
	n=read();
	for(int i=1;i<=n;++i)p[i].x=read(),p[i].y=read(),p[i].z=read();
	sort(p+1,p+1+n);
	for(int i=1;i<=n;++i)
	{
		f[i]=1;
		for(int j=1;j<i;++j)
			if(p[j].x<p[i].x&&p[j].y<p[i].y&&p[j].z<p[i].z&&f[j]+1>f[i])f[i]=f[j]+1;
	}
	for(int i=1;i<=n;++i)ans=max(ans,f[i]);
	printf("%d\n",ans);
	ans=0;
	for(int i=1;i<=n;++i)
		for(int j=1;j<i;++j)
			if(p[j].x<p[i].x&&p[j].y<p[i].y&&p[j].z<p[i].z)addedge(j,i);
	for(int i=1;i<=n;++i)
	{
		memset(vis,0,sizeof(vis));
		if(Hungary(i))++ans;
	}
	printf("%d\n",n-ans);
	return 0;
}
